package com.yst.util;

import lombok.Data;
import lombok.experimental.Accessors;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 类辅助单元
 */
public class ClassUtils {

    private static final String getPrefix="get";
    private static final String setPrefix="set";

    @Data
    @Accessors(chain = true)
    static class MethodField{
        private Method getMethod;
        private Method setMethod;
    }

    private static final Map<String,Map<String, MethodField>> methodFieldMap;

    private static final Map<String,Map<String, Field>> fieldMap;

    private static final Map<Class<?>,Object> classSingletonMap;

    private static final Map<Class<?>,Map<String,List<Method>>> classMethodMap;


    static {
        methodFieldMap=Collections.synchronizedMap(new HashMap<>());
        fieldMap=Collections.synchronizedMap(new HashMap<>());
        classSingletonMap=Collections.synchronizedMap(new HashMap<>());
        classMethodMap=Collections.synchronizedMap(new HashMap<>());
    }


    private static void getSupperClassFields(Class<?> clazz,Map<String,Field> outMap){
        if (clazz.getSuperclass()==null||clazz.getSuperclass()==Object.class) return;
        getSupperClassFields(clazz.getSuperclass(),outMap);
        Field[] fields=clazz.getSuperclass().getDeclaredFields();
        for(Field field:fields){
            outMap.put(field.getName(),field);
        }
    }
    /**
     * 获取类字段
     * @param clazz 由类获取类字段
     * @return 类字段
     */
    public static Map<String,Field> getClassFields(Class<?> clazz){
        if (Objects.isNull(clazz)) return null;
        if (fieldMap.containsKey(clazz.getName())){
            return Collections.unmodifiableMap(fieldMap.get(clazz.getName()));
        }
        else
        {
            Map<String,Field> map=new LinkedHashMap<>();
            getSupperClassFields(clazz,map);
            Field[] fields=clazz.getDeclaredFields();
            for(Field field:fields){
                map.put(field.getName(),field);
            }
            fieldMap.put(clazz.getName(),map);
            return Collections.unmodifiableMap(map);
        }
    }

    /**
     * 获取指定类型的字段，从类中
     * @param clazz
     * @param fieldClass
     * @return
     */
    public static List<Field> getClassFieldsByFieldClass(Class<?> clazz,Class<?> fieldClass){
        List<Field> fieldList=new ArrayList<>();
        Map<String,Field> classFields=getClassFields(clazz);
        for (Field field:classFields.values()){
            if (field.getType().isAssignableFrom(fieldClass)){
                fieldList.add(field);
            }
        }
        return fieldList;
    }

    /**
     * 获取类字段 get set方法
     * @param clazz 类
     * @return
     */
    public static Map<String,MethodField> getClassFieldMethods(Class<?> clazz){
        if (Objects.isNull(clazz)) return null;
        if (methodFieldMap.containsKey(clazz.getName())){
            return Collections.unmodifiableMap(methodFieldMap.get(clazz.getName()));
        }
        else
        {
            Map<String,MethodField> map=new LinkedHashMap<>();
            Method[] methods=clazz.getDeclaredMethods();
            for(Method method:methods){
                String mName=method.getName();
                if (mName.startsWith(getPrefix)) {
                    mName=mName.substring(getPrefix.length());
                    MethodField methodField=new MethodField();
                    methodField.getMethod=method;
                    String setMethodName=setPrefix+mName;
                    try {
                        methodField.setMethod = clazz.getMethod(setMethodName, method.getReturnType());
                    }
                    catch (Exception e){
                        //ignore error
                    }
                }
            }
            methodFieldMap.put(clazz.getName(),map);
            return Collections.unmodifiableMap(map);
        }
    }

    /**
     * 获取某类的单例
     * @param clazz 类型
     * @param <T> 泛型指示
     * @return 类实体
     * @throws Exception
     */
    public static <T> T getSingletonObject(Class<T> clazz) throws Exception{
        if (classSingletonMap.containsKey(clazz)){
            return (T)classSingletonMap.get(clazz);
        }
        else
        {
            T data = clazz.newInstance();
            classSingletonMap.put(clazz,data);
            return data;
        }
    }


    /**
     * 判断一个类型是否为基础类型的包装类
     * @param clazz
     * @return
     */
    public static Boolean isWrapperClass(Class clazz){
        try {
            return ((Class) clazz.getField("TYPE").get(null)).isPrimitive();
        }
        catch (Exception e){
            //ignore error
        }
        return false;
    }

    /**
     * 判断一个对象是否为基础数据
     * @param object
     * @return
     */
    public static Boolean isPrimitive(Object object){
        return isPrimitive(object.getClass());
    }

    /**
     * 判断一个类是否为基础数据类及其包装类
     * @param clazz
     * @return
     */
    public static Boolean isPrimitive(Class<?> clazz){
        return clazz.isPrimitive()||isWrapperClass(clazz);
    }

    /**
     * 由type 获取Class
     * @param type
     * @return
     */
    public static Class<?> fromType(Type type){
        try {
            if (type instanceof ParameterizedTypeImpl){
                return fromType(((ParameterizedTypeImpl) type).getActualTypeArguments()[0]);
            }
            else {
                return Class.forName(type.getTypeName());
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 判断一个类中是否有指定字段
     * @param clazz 指定的类
     * @param fields 要判断的字段
     * @return
     */
    public static Boolean classExistsField(Class<?> clazz,String...fields){
        Map<String,Field> fieldMap=getClassFields(clazz);
        for(String field:fields) {
            return fieldMap.containsKey(field);
        }
        return false;
    }

    /**
     * 获取类的指定名称的方法
     * @param clazz 类
     * @param methodName 方法名
     * @return 方法列表
     */
    public static List<Method> getMethods(Class<?> clazz,String methodName){
        if (classMethodMap.containsKey(clazz)){
            return classMethodMap.get(clazz).getOrDefault(methodName, Collections.emptyList());
        }
        else {
            Map<String,List<Method>> map=new HashMap<>();
            classMethodMap.put(clazz,map);
            Method[] methods=clazz.getDeclaredMethods();
            for (Method method:methods){
                List<Method> methodList;
                if (map.containsKey(method.getName())){
                   methodList=map.get(method.getName());
                }
                else {
                   methodList=new ArrayList<>();
                   map.put(methodName,methodList);
                }
                methodList.add(method);
            }
            return map.getOrDefault(methodName,Collections.emptyList());
        }
    }

    /**
     * 获取类的指定名称的静态方法
     * @param clazz 类
     * @param methodName 方法名
     * @return 方法列表
     */
    public static List<Method> getStaticMethods(Class<?> clazz,String methodName){
        List<Method> methods=getMethods(clazz,methodName);
        List<Method> result=new ArrayList<>();
        for(Method method:methods){
            if (Modifier.isStatic(method.getModifiers())){
                result.add(method);
            }
        }
        return Collections.unmodifiableList(result);
    }

    /**
     * 判断一个类是否有指定的静态方法
     * @param clazz 类
     * @param methodName 方法名
     * @return true=有，false=无
     */
    public static boolean haveStaticMethod(Class<?> clazz,String methodName){
        List<Method> methods=getMethods(clazz,methodName);
        for(Method method:methods){
            if (Modifier.isStatic(method.getModifiers())){
                return true;
            }
        }
        return false;
    }

    /**
     * 获取方法参数类型
     * @param method 方法
     * @param paramIndex 参数索引
     * @return 参数类型
     */
    public static Class<?> getMethodParamClass(Method method,int paramIndex){
        Parameter[] parameters= method.getParameters();
        if (paramIndex< parameters.length){
            return parameters[paramIndex].getType();
        }
        return null;
    }

    /**
     * 多类型继承判断
     * @param source 源类
     * @param classes 多类型
     * @return true 是,false 否
     */
    public static boolean isAssignableFrom(Class<?> source,Class<?>...classes){
        for(Class<?> c:classes){
            if (source.isAssignableFrom(c)) return true;
        }
        return false;
    }

    /**
     * 获取对象的指定名称方法
     * @param object 对象
     * @param methodName 方法名
     * @param args 参数数组
     * @return 方法
     */
    public static Method getMethod(Object object,String methodName,Object[] args){
        if (Objects.isNull(object)) return null;
        try {
            return object.getClass().getDeclaredMethod(methodName, Arrays.stream(args).map(Object::getClass)
                    .collect(Collectors.toList()).toArray(new Class[]{}));
        }
        catch (Exception e){
            e.printStackTrace();
            //ignore error
        }
        return null;
    }

}
